﻿// -----------------------------------------------------------------------
// <copyright file="IndexedList.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace WMM.Tapioca.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public interface IIndexable
    {
        int Id { get; }
        int Name { get; }
    }

    public class IndexedList<T> : IList<T> where T : IIndexable
    {
        List<T> _mainCollectionById = null;
        List<T> _mainCollectionByName = null;
        SortedDictionary<int, T> _indexedCollectionById = null;
        SortedDictionary<int, T> _indexedCollectionByName = null;
        object _SyncRoot = new object();

        public IndexedList()
        {
            _indexedCollectionById = new SortedDictionary<int, T>();
            _indexedCollectionByName = new SortedDictionary<int, T>();
            _mainCollectionById = new List<T>();
            _mainCollectionByName = new List<T>();
        }

        public IndexedList(List<T> list)
        {
            _indexedCollectionById = new SortedDictionary<int, T>();
            _indexedCollectionByName = new SortedDictionary<int, T>();
            _mainCollectionById = new List<T>();
            _mainCollectionByName = new List<T>();

            foreach (T type in list)
            {
                _indexedCollectionById.Add(type.Id, type);
                _mainCollectionById.Add(type);

                _indexedCollectionByName.Add(type.Name, type);
                _mainCollectionByName.Add(type);
            }
        }


        public int IndexOf(T item)
        {
            lock (_SyncRoot)
                return _mainCollectionById.IndexOf(item);
        }


        public void Insert(int index, T item)
        {
            if (_indexedCollectionById.ContainsKey(item.Id))
            {
                throw new ArgumentException(string.Format("Item with key '{0}' already exists.", item.Id));
            }

            lock (_SyncRoot)
            {
                _mainCollectionById.Insert(index, item);
                _indexedCollectionById.Add(item.Id, item);
            }
        }


        public void RemoveAt(int index)
        {
            T item = _mainCollectionById[index];

            lock (_SyncRoot)
            {
                _mainCollectionById.Remove(item);
                _indexedCollectionById.Remove(item.Id);
            }
        }


        public T this[int index]
        {
            get { return _indexedCollectionById[index]; }

            set
            {
                T item = _mainCollectionById[index];
                lock (_SyncRoot)
                {
                    if (item.Id == value.Id)
                    {
                        _mainCollectionById[index] = value;
                        _indexedCollectionById[value.Id] = value;
                    }
                    else
                    {
                        if (_indexedCollectionById.ContainsKey(value.Id))
                            throw new ArgumentException(string.Format("Item with key '{0}' already exists.", value.Id));
                        _mainCollectionById.Remove(item);
                        _mainCollectionById.Add(value);
                        _indexedCollectionById.Remove(item.Id);
                        _indexedCollectionById.Add(value.Id, value);
                    }
                }
            }
        }


        public T GetItemByName(int index)
        {
            return _indexedCollectionByName[index]; 
        }


        public List<T> GetList()
        {
            return _mainCollectionById;
        }


        public void Add(T item)
        {
            if (_indexedCollectionById.ContainsKey(item.Id))
            {
                return;
            }

            lock (_SyncRoot)
            {
                _mainCollectionById.Add(item);
                _indexedCollectionById.Add(item.Id, item);
            }
        }


        public void AddById(T item)
        {
            if (_indexedCollectionById.ContainsKey(item.Id))
            {
                return;
                //throw new ArgumentException(string.Format("Item with key '{0}' already exists.", item.Id));
            }

            lock (_SyncRoot)
            {
                _mainCollectionById.Add(item);
                _indexedCollectionById.Add(item.Id, item);
            }           
        }

        public void AddByName(T item)
        {
            if (_indexedCollectionByName.ContainsKey(item.Name))
            {
                return;
            }

            lock (_SyncRoot)
            {
                _mainCollectionByName.Add(item);
                _indexedCollectionByName.Add(item.Name, item);
            }
        }


        public void Clear()
        {
            lock (_SyncRoot)
            {
                _mainCollectionById.Clear();
                _indexedCollectionById.Clear();
            }
        }


        public bool Contains(T item)
        {
            lock (_SyncRoot)
                return _indexedCollectionById.ContainsKey(item.Id);
        }


        public bool ContainsKey(int key)
        {
            lock (_SyncRoot)
                return _indexedCollectionById.ContainsKey(key);
        }


        public bool ContainsKeyById(int key)
        {
            lock (_SyncRoot)
                return _indexedCollectionById.ContainsKey(key);
        }


        public bool ContainsKeyByName(int key)
        {
            lock (_SyncRoot)
                return _indexedCollectionByName.ContainsKey(key);
        }


        public void CopyTo(T[] array, int arrayIndex)
        {
            lock (_SyncRoot)
                _mainCollectionById.CopyTo(array, arrayIndex);
        }


        public int Count
        {
            get
            {
                lock (_SyncRoot)
                    return _mainCollectionById.Count();
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(T item)
        {
            lock (_SyncRoot)
            {
                return _mainCollectionById.Remove(item) &&
                _indexedCollectionById.Remove(item.Id);
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _mainCollectionById.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
}
